package com.rede.didiok.sms.event;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rede.didiok.common.constant.conf.SysConf;
import com.rede.didiok.common.constant.global.Constants;
import com.rede.didiok.common.convert.AggEsDocConvert;
import com.rede.didiok.common.domainEvent.DomainEvent;
import com.rede.didiok.common.domainEvent.EntityType;
import com.rede.didiok.common.dto.admin.AdminDto;
import com.rede.didiok.common.dto.admin.SystemConfigDto;
import com.rede.didiok.common.dto.rank.RankMemberDto;
import com.rede.didiok.common.dto.user.NoticeDto;
import com.rede.didiok.common.dto.user.UserDto;
import com.rede.didiok.common.enums.*;
import com.rede.didiok.common.exception.exceptionType.FeignException;
import com.rede.didiok.common.holder.RequestHolder;
import com.rede.didiok.common.schema.AggEsDoc;
import com.rede.didiok.common.utils.R;
import com.rede.didiok.common.utils.StringUtils;
import com.rede.didiok.sms.event.annotation.EventType;
import com.rede.didiok.sms.event.service.AbstractEventHandler;

/**
 * 博客事件处理器
 */
@Slf4j
@Component
@EventType({EntityType.RANK_MEMBER})
public class RankMemberEventHandler extends AbstractEventHandler {

    @Override
    public void doEventHandler(DomainEvent domainEvent) {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        RankMemberDto rankMemberDto = objectMapper.convertValue(domainEvent.getEntity(), RankMemberDto.class);
        switch (domainEvent.getEventAction()) {
            case RANK_MEMBER_ADD: {
                this.add(rankMemberDto, domainEvent.isAdminOperate());
            }
            break;
            case RANK_MEMBER_EDIT: {
                this.edit(rankMemberDto, domainEvent.isAdminOperate());
            }
            break;
            case RANK_MEMBER_DELETE: {
                this.delete(rankMemberDto, domainEvent.isAdminOperate());
            }
            break;
            case RANK_MEMBER_AUDIT: {
                this.audit(rankMemberDto, domainEvent.isAdminOperate());
            }
            break;
            case RANK_MEMBER_PUBLISH: {
                this.publish(rankMemberDto, domainEvent.isAdminOperate());
            }
            break;
        }
    }

    /**
     * 博客新增事件
     *
     * @param rankMemberDto
     * @param isAdminOperate
     */
    public void add(RankMemberDto rankMemberDto, boolean isAdminOperate) {
        /**
         * 新增情况下 审核通过  说明是免审
         * 一般都不会走这一层
         * 博主属于特殊角色
         * 在新增博客选择发布时需要给与积分
         */
        if (AuditStatusEnum.AGREE.equals(rankMemberDto.getAuditStatus()) && PublishEnum.PUBLISH.equals(rankMemberDto.getIsPublish())) {
            /**
             * 站内信通知
             */
            this.sendNotice(rankMemberDto);

        }

        /**
         * 新增时候选择发布的博客都给与网站管理员发送邮件提示
         */
        if (PublishEnum.PUBLISH.equals(rankMemberDto.getIsPublish())) {
            /**
             * 通知运营
             */
            this.sendEmailToManager(rankMemberDto);
        }

        //发布后台系统消息通知
        if (!AuditStatusEnum.AGREE.equals(rankMemberDto.getAuditStatus()) || !PublishEnum.PUBLISH.equals(rankMemberDto.getIsPublish())) {
            this.sendBlackNotice(rankMemberDto);
        }


        // 更新ES索引
        this.syncAggEsDoc(rankMemberDto, OperateEnum.ADD);
    }

    /**
     * 博客修改事件
     *
     * @param rankMemberDto
     * @param isAdminOperate
     */
    private void edit(RankMemberDto rankMemberDto, boolean isAdminOperate) {

        // 更新索引
        this.syncAggEsDoc(rankMemberDto, OperateEnum.EDIT);
    }

    /**
     * 博客审核事件
     *
     * @param rankMemberDto
     * @param isAdminOperate
     */
    private void audit(RankMemberDto rankMemberDto, boolean isAdminOperate) {
        if (PublishEnum.PUBLISH.equals(rankMemberDto.getIsPublish()) && AuditStatusEnum.AGREE.equals(rankMemberDto.getAuditStatus())) {
            /**
             * 站内信
             */
            this.sendNotice(rankMemberDto);

        }

        /**
         * 对作者发送邮件和站内信
         */
        if (rankMemberDto.getCreateUserUid() != null) {
            R result = userFeignClient.getUserById(rankMemberDto.getCreateUserUid());
            UserDto user;
            if(result.getCode()==0){
                user = result.getData(new TypeReference<UserDto>(){});
            }else {
                // 获取异常信息
                throw new FeignException("feign调用异常（根据用户id找用户）：失败");
            }
            StringBuilder text = new StringBuilder();
            text.append(String.format("您的稿件 %s ", rankMemberDto.getTitle()));
            if (AuditStatusEnum.REJECT.equals(rankMemberDto.getAuditStatus())) {
                text.append("审核未通过，未通过原因：" + rankMemberDto.getRejectReason());
                rabbitMqUtil.sendSimpleEmail(user.getEmail(), text.toString());
            } else if (AuditStatusEnum.AGREE.equals(rankMemberDto.getAuditStatus())) {
                text.append("审核已通过");
                rabbitMqUtil.sendSimpleEmail(user.getEmail(), text.toString());
            } else {
                log.error("审核状态异常,稿件为{}", rankMemberDto.getUid());
            }
            // 发送文章审核通过
            sendAuditNotice(rankMemberDto, text.toString());
        }
        // 更新索引
        this.syncAggEsDoc(rankMemberDto, OperateEnum.EDIT);
    }

    /**
     * 博客发布事件
     *
     * @param rankMemberDto
     * @param isAdminOperate
     */
    private void publish(RankMemberDto rankMemberDto, boolean isAdminOperate) {
        /**
         * 站内信
         */
        this.sendNotice(rankMemberDto);
        /**
         * 管理员发布
         */
        if (isAdminOperate) {
            /**
             * 管理员下架的话
             */
            if (PublishEnum.NO_PUBLISH.equals(rankMemberDto.getIsPublish())) {
                HttpServletRequest request = RequestHolder.getRequest();
                R adminResult = adminFeignClient.getAdminByUid(request.getAttribute(SysConf.ADMIN_UID).toString());
                AdminDto admin = null;
                if (adminResult.getCode() == 0) {
                    admin = adminResult.getData(new TypeReference<AdminDto>(){});
                }else {
                    throw new FeignException("feign调用异常（通过 id 获取管理员）：失败");
                }
                rankMemberDto.setAuditName(admin.getNickname());
                rankMemberDto.setAuditStatus(AuditStatusEnum.REJECT);
                rankMemberDto.setRejectReason("管理员下架文章");
                rankMemberDto.setAuditTime(new Date());

                R result = userFeignClient.getUserById(rankMemberDto.getCreateUserUid());
                UserDto user;
                if(result.getCode()==0){
                    user = result.getData(new TypeReference<UserDto>(){});
                }else {
                    // 获取异常信息
                    throw new FeignException("feign调用异常（根据用户id找用户）：失败");
                }
                StringBuilder text = new StringBuilder();
                text.append(String.format("<p>亲爱的的博主，您的博客稿件 %s 被管理员下架，如有疑问请联系管理员</p>", rankMemberDto.getMemberName()));
                rabbitMqUtil.sendSimpleEmail(user.getEmail(), text.toString());
                // 发送站内信，文章被下架
                sendAuditNotice(rankMemberDto, text.toString());
                return;
            }

        } else {
            /**
             * 用户发布
             */
            if (PublishEnum.PUBLISH.equals(rankMemberDto.getIsPublish())) {
                /**
                 * 通知运营
                 */
                sendEmailToManager(rankMemberDto);
            }
        }

        /**
         * 更新索引
         */
        this.syncAggEsDoc(rankMemberDto, OperateEnum.EDIT);
    }

    /**
     * 博客删除事件
     *
     * @param rankMemberDto
     * @param isAdminOperate
     */
    private void delete(RankMemberDto rankMemberDto, boolean isAdminOperate) {

        // 移除所有包含该博客的专题元素
        List<String> memberUidList = new ArrayList<>(Constants.NUM_ONE);
        memberUidList.add(rankMemberDto.getUid());

        // 移除该文章下所有评论
        R result = rankFeignClient.batchDeleteCommentByMemberUid(memberUidList);

        /**
         * 更新索引
         */
        this.syncAggEsDoc(rankMemberDto, OperateEnum.DELETE);
    }

    /**
     * 发送通知
     *
     * @param rankMemberDto
     */
    private void sendNotice(RankMemberDto rankMemberDto) {
        /**
         * 上架操作
         */
        if (AuditStatusEnum.AGREE.equals(rankMemberDto.getAuditStatus()) && PublishEnum.PUBLISH.equals(rankMemberDto.getIsPublish())) {
            /**
             * 站内信通知
             */
            if (rankMemberDto.getCreateUserUid() != null) {
                asyncService.executeAsyncBusinessNotice(false, rankMemberDto.getCreateUserUid(), rankMemberDto.getUid(), BusinessTypeEnum.RANK_MEMBER.getCode());
            } else {
                asyncService.executeAsyncBusinessNotice(true, rankMemberDto.getAdminUid(), rankMemberDto.getUid(), BusinessTypeEnum.RANK_MEMBER.getCode());
            }
        }
    }

    /**
     * 发送后台通知
     *
     * @param rankMemberDto
     */
    private void sendBlackNotice(RankMemberDto rankMemberDto) {
        /**
         * 进入审批流程的博客 会给后台发送站内信通知
         */
        if (!AuditStatusEnum.AGREE.equals(rankMemberDto.getAuditStatus()) || !PublishEnum.PUBLISH.equals(rankMemberDto.getIsPublish())) {
            asyncService.executeAsyncBusinessBlackNotice(false, rankMemberDto.getCreateUserUid(), rankMemberDto.getUid(), BusinessTypeEnum.RANK_MEMBER.getCode(), rankMemberDto.getMemberName());
        }
    }

    /**
     * 发送邮件通知
     *
     * @param rankMemberDto
     */
    private void sendEmailToManager(RankMemberDto rankMemberDto) {
        /**
         * 发送邮件用纸
         */
        SystemConfigDto systemConfig = systemConfigUtil.getSystemConfig();
        if (systemConfig != null && OpenStatusEnum.OPEN.equals(systemConfig.getStartEmailNotification())) {
            if (StringUtils.isNotEmpty(systemConfig.getEmail())) {
                log.info("发送邮件通知");
                String emailContent = "收到用户的投稿文章：" + rankMemberDto.getMemberName();
                rabbitMqUtil.sendSimpleEmail(systemConfig.getEmail(), emailContent);
            } else {
                log.error("网站没有配置通知接收的邮箱地址！");
            }
        }
    }

    /**
     * 同步AggEsDoc
     *
     * @param rankMemberDto
     * @return
     */
    private void syncAggEsDoc(RankMemberDto rankMemberDto, OperateEnum operate) {
        AggEsDoc aggEsDoc = AggEsDocConvert.convertRankMember(rankMemberDto);
        if (OperateEnum.ADD == operate && aggEsDoc.getOid() == null) {
            log.info("syncAggEsDoc: aggEsDoc oid is nil");
            R memberResult = rankFeignClient.getRankMemberById(rankMemberDto.getUid());
            RankMemberDto memberDtoTemp;
            if (memberResult.getCode() == 0) {
                memberDtoTemp = memberResult.getData(new TypeReference<RankMemberDto>(){});
            }else {
                throw new FeignException("feign调用异常（根据id获取榜单成员）：失败");
            }
            if (memberDtoTemp != null) {
                aggEsDoc.setOid(memberDtoTemp.getOid());
            }
            /**
             * 更新索引
             */
        }
        asyncService.executeAsyncUpdateEsAndRedis(aggEsDoc, operate);
    }

    /**
     * 差集(基于java8新特性)优化解法2 适用于大数据量
     * * 求List1中有的但是List2中没有的元素
     *
     * @param list1
     * @param list2
     * @return
     */
    private List<String> subList(List<String> list1, List<String> list2) {
        Map<String, String> tempMap = list2.parallelStream().collect(Collectors.toMap(Function.identity(), Function.identity(), (oldData, newData) -> newData));
        return list1.parallelStream().filter(str -> {
            return !tempMap.containsKey(str);
        }).collect(Collectors.toList());
    }

    /**
     * 发送审核通知
     *
     * @param rankMemberDto
     * @param text
     */
    private void sendAuditNotice(RankMemberDto rankMemberDto, String text) {
        log.info("[sendAuditNotice] 发送博客审核通知");
        NoticeDto noticeDto = new NoticeDto();
        noticeDto.setCreateUserUid(rankMemberDto.getCreateUserUid());
        noticeDto.setContent(text);
        noticeDto.setNoticeType(NoticeTypeEnum.SYSTEM.getCode());
        noticeDto.setBusinessType(BusinessTypeEnum.RANK_MEMBER_AUDIT.getCode());
        noticeDto.setBusinessUid(rankMemberDto.getUid());
        noticeDto.setIsBack(Constants.NUM_ZERO);
        asyncService.executeAsyncBusinessNotice(noticeDto);
    }

}
